package com.anji.plus.gaea.init;

import com.anji.plus.gaea.annotation.Permission;
import com.anji.plus.gaea.annotation.Permissions;
import com.anji.plus.gaea.constant.Enabled;
import com.anji.plus.gaea.controller.GaeaBootController;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import static com.anji.plus.gaea.constant.GaeaConstant.*;

/**
 * 获取所有请求Url，供权限管理使用
 *
 * @author lr
 * @since 2021-02-26
 */
public class InitRequestUrlMappings {

    @Autowired
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    @Value("${spring.application.name:}")
    private String applicationName;

    /**
     * 获取当前应用中所有请求信息
     *
     * @return
     */
    public List<RequestInfo> getRequestInfos(Integer scanAnnotation) {
        //请求所有请求处理方法
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
        List<RequestInfo> list = new ArrayList<>();
        handlerMethods.entrySet().stream().forEach(entry -> {
            List<Permission> annotations = new ArrayList<>();
            HandlerMethod value = entry.getValue();
            //忽略到自带的GaeaBootController
            if (value.getBeanType() == GaeaBootController.class) {
                return;
            }
            //判断是否只扫描带注解
            if (Enabled.YES.getValue().equals(scanAnnotation)) {
                Method method = value.getMethod();
                Class<?> beanType = value.getBeanType();
                if (beanType.isAnnotationPresent(Permission.class) &&
                        (method.isAnnotationPresent(Permission.class) || (method.isAnnotationPresent(Permissions.class)))) {
                    Permission type = beanType.getAnnotation(Permission.class);
                    Permission[] permissions = method.getAnnotationsByType(Permission.class);
                    for (Permission mtd : permissions) {
                        RequestInfo requestInfo = new RequestInfo();
                        requestInfo.setEnabled(mtd.enabled()&&type.enabled());
                        requestInfo.setDependsOn(mtd.dependsOn());
                        if (StringUtils.isNotBlank(mtd.superCode())) {
                            requestInfo.setMenuCode(mtd.superCode());
                            requestInfo.setAuthCode(mtd.superCode() + REDIS_SPLIT + mtd.code());
                        } else if(StringUtils.isNotBlank(type.superCode())) {
                            requestInfo.setMenuCode(type.superCode());
                            if(StringUtils.isEmpty(mtd.superCode())) {
                                requestInfo.setAuthCode(type.code() + REDIS_SPLIT + mtd.code());
                            }else {
                                requestInfo.setAuthCode(mtd.superCode() + REDIS_SPLIT + mtd.code());
                            }
                        } else {
                            requestInfo.setMenuCode(type.code());
                            requestInfo.setAuthCode(type.code() + REDIS_SPLIT + mtd.code());
                        }

                        requestInfo.setAuthName(type.name() + "-" + mtd.name());
                        annotations.add(mtd);

                        requestInfo.setBeanName(value.getBean().toString());
                        requestInfo.setApplicationName(applicationName);

                        RequestMappingInfo m = entry.getKey();

                        Optional<RequestMethod> requestMethodOptional = m.getMethodsCondition()
                                .getMethods().stream().findFirst();
                        //判断路由上是否指定请求方法如：GET/POST等，没有忽略
                        if (!requestMethodOptional.isPresent()) {
                            return;
                        }

                        //请求方法
                        RequestMethod requestMethod = requestMethodOptional.get();
                        PatternsRequestCondition cond = m.getPatternsCondition();
                        if(Objects.isNull(cond)){
                            continue;
                        }
                        Optional<String> pathOptional = cond.getPatterns().stream().findFirst();
                        if (!pathOptional.isPresent()) {
                            return;
                        }

                        String path = pathOptional.get();
                        //当出现动态参数时将/{xxx}替换为/*
                        if (path.contains(URL_MARK)) {
                            path = path.replaceAll(URL_REGEX, PATTERN_SIGN);
                        }
                        requestInfo.setPath(requestMethod + URL_SPLIT + path);
                        list.add(requestInfo);
                    }
                } else {
                    return;
                }
            } else {
                RequestInfo requestInfo = new RequestInfo();
                requestInfo.setAuthCode(value.getBean() + URL_SPLIT + value.getMethod().getName());
                requestInfo.setBeanName(value.getBean().toString());
                requestInfo.setApplicationName(applicationName);
                requestInfo.setEnabled(true);
                RequestMappingInfo requestMappingInfo = entry.getKey();

                Optional<RequestMethod> requestMethodOptional = requestMappingInfo.getMethodsCondition()
                        .getMethods().stream().findFirst();

                //判断路由上是否指定请求方法如：GET/POST等，没有忽略
                if (!requestMethodOptional.isPresent()) {
                    return;
                }

                //请求方法
                RequestMethod requestMethod = requestMethodOptional.get();
                PatternsRequestCondition cond = requestMappingInfo.getPatternsCondition();
                if(Objects.isNull(cond)){
                    return;
                }
                Optional<String> pathOptional = cond.getPatterns().stream().findFirst();
                if (!pathOptional.isPresent()) {
                    return;
                }
                String path = pathOptional.get();
                //当出现动态参数时将/{xxx}替换为/**
                if (path.contains(URL_MARK)) {
                    path = path.replaceAll(URL_REGEX, URL_REPLACEMENT);
                }

                requestInfo.setPath(requestMethod + URL_SPLIT + path);
                list.add(requestInfo);
            }
        });

        return list;
    }

    /**
     * 请求信息
     */
    public static class RequestInfo {

        /**
         * 应用名称
         */
        private String applicationName;

        /**
         * 实例Bean
         */
        private String beanName;

        /**
         * 菜单编码
         */
        private String menuCode;

        /**
         * 权限编号
         */
        private String authCode;

        /**
         * 权限名称
         */
        private String authName;

        /**
         * 请求路径path
         */
        private String path;
        /**
         * 权限码是否启用
         */
        private boolean enabled;
        /**
         * 该权限码 依赖的api清单，GET#/ab/c,POST#/de/f/c,用户授权后，对应api清单自动授权
         */
        private String dependsOn;

        public String getDependsOn() {
            return dependsOn;
        }

        public void setDependsOn(String dependsOn) {
            this.dependsOn = dependsOn;
        }

        public boolean isEnabled() {
            return enabled;
        }

        public void setEnabled(boolean enabled) {
            this.enabled = enabled;
        }

        public String getApplicationName() {
            return applicationName;
        }

        public void setApplicationName(String applicationName) {
            this.applicationName = applicationName;
        }

        public String getAuthCode() {
            return authCode;
        }

        public void setAuthCode(String authCode) {
            this.authCode = authCode;
        }

        public String getAuthName() {
            return authName;
        }

        public void setAuthName(String authName) {
            this.authName = authName;
        }

        public String getPath() {
            return path;
        }

        public void setPath(String path) {
            this.path = path;
        }

        public String getBeanName() {
            return beanName;
        }

        public void setBeanName(String beanName) {
            this.beanName = beanName;
        }

        public String getMenuCode() {
            return menuCode;
        }

        public void setMenuCode(String menuCode) {
            this.menuCode = menuCode;
        }
    }
}
